<template>
  <div class="p-4 max-w-4xl mx-auto">
    <h1 class="text-2xl font-semibold mb-4">网页图片提取器</h1>

    <div class="mb-4 p-4 bg-gray-50 border border-gray-200 rounded">
      <h2 class="text-lg font-medium mb-2">使用说明</h2>
      <ol class="list-decimal list-inside text-sm text-gray-700 space-y-1 mb-2">
        <li>在输入框中填写要提取图片的网页地址（请以 <code>https://</code> 或 <code>http://</code> 开头）。</li>
        <li>点击 <strong>抓取</strong> 按钮，等待服务器抓取并返回页面中的图片列表。</li>
        <li>在结果中预览图片：点击图片或勾选复选框以选择/取消选择。</li>
        <li>使用顶部的操作按钮可以单张下载、批量下载或将已选图片打包为 ZIP 下载。</li>
        <li>若图片加载失败，可尝试打开原始网页确认图片是否受保护或被防盗链。</li>
      </ol>
      <h3 class="text-sm font-medium mb-1">注意事项</h3>
      <ul class="list-disc list-inside text-sm text-gray-700 space-y-1">
        <li>请遵守版权和使用条款：仅在拥有权利或得到授权时下载与使用图片。</li>
        <li>某些网站对爬虫或跨域请求有限制，可能导致抓取失败或图片无法加载。</li>
        <li>大量或频繁抓取同一站点可能触发目标站点的访问限制，请适度使用。</li>
        <li>私有或需登录的页面无法在不提供凭证的情况下提取图片。</li>
        <li>若遇到 CORS 或代理问题，可尝试使用“下载为 ZIP”或联系管理员。</li>
      </ul>
    </div>

    <div class="flex gap-2 mb-4">
      <input v-model="url" placeholder="输入网页地址 (以 https:// 开头)" class="flex-1 p-2 border rounded" />
      <button @click="fetchImages" :disabled="loading || !url" class="px-4 py-2 bg-blue-600 text-white rounded">抓取</button>
    </div>

    <div v-if="error" class="mb-4 text-red-600">错误: {{ error }}</div>
    <div v-if="loading" class="mb-4">抓取中，请稍候…</div>
    <div v-if="images.length" class="mb-4">找到 {{ images.length }} 张图片</div>

    <div v-if="images.length" class="mb-6">
      <button @click="downloadSelected" class="px-3 py-1 mr-2 bg-green-600 text-white rounded">下载已选 ({{ selectedCount }})</button>
      <button @click="downloadSelectedZip" class="px-3 py-1 mr-2 bg-indigo-600 text-white rounded">下载为 ZIP</button>
      <button @click="selectAll" class="px-3 py-1 mr-2 bg-gray-200 rounded">全选</button>
      <button @click="clearSelection" class="px-3 py-1 bg-gray-200 rounded">清除选择</button>
    </div>

    <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
      <div v-for="(img, idx) in images" :key="img" class="border rounded overflow-hidden relative">
        <img :src="getProxyUrl(img)" class="w-full h-40 object-cover cursor-pointer" @click="toggleSelect(img)" :alt="`img-${idx}`" @error="onImgError($event, idx)"/>
        <div class="p-2 flex items-center justify-between">
          <button @click="downloadImage(img)" class="text-sm text-blue-600">下载</button>
          <label class="inline-flex items-center">
            <input type="checkbox" :checked="isSelected(img)" @change="toggleSelect(img)" />
          </label>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

const url = ref('')
const images = ref<string[]>([])
const loading = ref(false)
const error = ref('')
const selected = ref(new Set<string>())

function isSelected(u: string) {
  return selected.value.has(u)
}

function toggleSelect(u: string) {
  if (selected.value.has(u)) selected.value.delete(u)
  else selected.value.add(u)
}

const selectedCount = computed(() => selected.value.size)

async function fetchImages() {
  if (!url.value) return
  loading.value = true
  error.value = ''
  images.value = []
  selected.value = new Set()
  try {
    const res: any = await $fetch('/api/extract-images', {
      method: 'POST',
      body: { url: url.value }
    })
    if (res && res.error) {
      error.value = res.error
    } else {
      images.value = (res.images || [])
    }
  } catch (e: any) {
    error.value = e && e.message ? e.message : String(e)
  } finally {
    loading.value = false
  }
}

function getProxyUrl(u: string) {
  return `/api/proxy-image?url=${encodeURIComponent(u)}`
}

async function downloadImage(u: string) {
  try {
    const resp = await fetch(getProxyUrl(u))
    if (!resp.ok) throw new Error('下载失败')
    const blob = await resp.blob()
    const ext = (u.split('.').pop() || 'jpg').split('?')[0]
    const filename = `image.${ext}`
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = filename
    document.body.appendChild(link)
    link.click()
    link.remove()
    setTimeout(() => URL.revokeObjectURL(link.href), 5000)
  } catch (e) {
    alert('下载失败: ' + e)
  }
}

async function downloadSelected() {
  const arr = Array.from(selected.value)
  if (!arr.length) return alert('请先选择图片')
  // 下载顺序进行，避免同时触发太多弹窗
  for (const u of arr) {
    // eslint-disable-next-line no-await-in-loop
    await downloadImage(u)
    // 小间隔
    // eslint-disable-next-line no-await-in-loop
    await new Promise(r => setTimeout(r, 300))
  }
}

async function downloadSelectedZip() {
  const arr = Array.from(selected.value)
  if (!arr.length) return alert('请先选择图片')
  try {
    const resp = await fetch('/api/download-zip', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ images: arr, name: 'images' })
    })
    if (!resp.ok) throw new Error('zip 打包失败')
    const buffer = await resp.arrayBuffer()
    const blob = new Blob([buffer], { type: 'application/zip' })
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = 'images.zip'
    document.body.appendChild(link)
    link.click()
    link.remove()
    setTimeout(() => URL.revokeObjectURL(link.href), 5000)
  } catch (e) {
    alert('ZIP 下载失败: ' + e)
  }
}

function selectAll() {
  selected.value = new Set(images.value)
}

function clearSelection() {
  selected.value = new Set()
}

function onImgError(e: Event, idx: number) {
  const el = e.target as HTMLImageElement
  el.style.opacity = '0.4'
}
</script>

<style scoped>
.grid img { transition: transform .12s ease; }
.grid img:hover { transform: scale(1.03); }
</style>
